Java基础语法学习笔记

45次阅读
没有评论

共计 18614 个字符,预计需要花费 47 分钟才能阅读完成。

准备工作

JRE = JVM + 核心类库。JRE(Java 运行环境),JVM(Java 虚拟机,真正运行 Java 程序的地方),核心类库(Java 已经写好的程序,供程序员调用)。

JDK = JRE + 开发工具。JDK(Java 开发工具包),开发工具(java、javac 等)。

安装 JDK

官网下载:https://www.oracle.com/cn/java/technologies/downloads/,为了便于管理,建议采用 Compressed Archive 版本。

长期支持版 (Long-Term Support, LTS):

  • JDK 8 LTS 2014 年 3 月
  • JDK 11 LTS 2018 年 9 月
  • JDK 17 LTS 2021 年 9 月
  • JDK 21 LTS 2023 年 9 月

配置环境变量

配置 Path 环境变量:右键「此电脑」→「属性」→「高级系统设置」→「环境变量」→「系统变量」→「新建」,变量名:JAVA_HOME,变量值:填写 JDK 安装路径,如:C:\dev\java\jdk-21.0.4。

双击系统变量下的 Path 环境变量,新建 %JAVA_HOME%\bin 并上移到第一位,最后点击确定即可。

测试 JDK

打开 CMD 窗口,输入:java -version,测试 JDK 是否安装成功。

业内大多数程序员都在遵守阿里巴巴的命名规则:《阿里巴巴 Java 开发手册(终极版)》。

IDEA

IDEA 全称 IntelliJ IDEA,是用于 Java 语言开发的集成环境,是业界公认的目前用于 Java 程序开发最好的工具。

IntelliJ IDEA 官网:https://www.jetbrains.com/idea/download/

IntelliJ IDEA 正版专属激活码领取(永久更新):
JETBRA.IN CHECKER | IPFS:https://3.jetbra.in/

项目结构

  • project(项目、工程)
  • module(模块)
  • package(包)
  • class(类)

project 中可以创建多个 module,module 中可以创建多个 package,package 中可以创建多个 class。

IDEA 常用快捷键

  • main:快速输入相关代码
  • Ctrl + D:复制当前行到下一行
  • Ctrl + X:删除所在行
  • Ctrl + Alt + L:格式化代码
  • Shift + Alt + ↑,↓:上下移动当前代码
  • Ctrl + /,Ctrl + Shift + /:代码注释
  • Alt + Enter:强制类型转换

基础语法

字面量:数据在程序中的书写格式。

标识符

就是给类,方法,变量等起的名字。

标识符命名规则,硬性要求:

  • 必须由数字、字母、下划线_、美元符号 $ 组成
  • 数字不能开头
  • 不能是关键字
  • 区分大小写

软性建议:

  • 小驼峰命名法:适用于变量名和方法名
  • 大驼峰命名法:适用于类名

数据类型

基本数据类型,4 大类 8 种:

  • 整型:byte 1 字节,short 2 字节,int 默认 4 字节,long 8 字节
  • 浮点型:float,4 字节;double 默认,8 字节
  • 字符型:char,2 字节
  • 布尔型:boolean,1 字节
// 默认是 int 类型,字面量后面加 L/l 变成 long 类型
long lg = 100L;
// 默认是 double 类型,字面量后面加 F/f 变成 float 类型
float f = 3.14F;

引用数据类型,String、数组。

获取键盘输入

public class HelloWorld {public static void main(String[] args) {
        // 得到一个键盘扫描器对象
        Scanner sc = new Scanner(System.in);
        // 调用 sc,接收键盘输入数据
        System.out.println("姓名:");
        String name = sc.next();
        System.out.println("年龄:");
        int age = sc.nextInt();
        System.out.println(name + age + "岁");
    }
}

Scanner 键盘录入字符串:

  • next():遇到空格或 Tab 键就不再录入,数据可能录入不完整
  • nextLine():以回车作为录入结束标记,之前调用过 nextInt(), nextDouble(), nextFloat()等 nextLine()就不干活了
  • 使用方案:如果键盘录入的数据,全都是字符串,直接 nextLine()。如果除了字符串还有其他类型,使用 next()

分支结构

switch 注意事项:

  • 表达式类型只能是 byte、short、int、char。JDK 5 开始支持枚举,JDK 7 开始支持 String,不支持 long、float、double
  • case 值不允许重复,且只能是字面量,不能是变量
  • 不要忘记写 break,否则会出现穿透现象

循环结构

for 循环中控制循环的变量只能在循环中使用,while 循环中控制循环的变量在循环后还可以继续使用。

生成随机数

public class HelloWorld {public static void main(String[] args) {System.out.println(createCode(4));
    }

    public static String createCode(int n) {
        // 创建 Random 对象,用于生成随机数
        Random r = new Random();
        // 每位随机字符
        String code = "";
        for (int i = 1; i <= n; i++) {int type = r.nextInt(3);
            switch (type) {
                case 0:
                    // 随机一个数字字符 0-9
                    code += r.nextInt(10);
                    break;
                case 1:
                    // 随机一个大写字符 A 65 Z 65+25 (0-25)+65
                    char ch1 = (char) (r.nextInt(26) + 65);
                    code += ch1;
                    break;
                case 2:
                    // 随机一个小写字符 a 97 z 97+25 (0-25)+97
                    char ch2 = (char) (r.nextInt(26) + 97);
                    code += ch2;
                    break;
            }
        }

        return code;
    }
}

内存分配

Java 为了便于虚拟机执行 Java 程序,将虚拟机内存划分为:

  • 方法区:字节码文件先加载到这里
  • 栈:方法运行时所进入的内存,变量也在这一块区域中
  • 堆:new 出来的东西,会在这块内存中开辟空间并分配地址。静态成员变量
  • 本地方法栈
  • 程序计数器

面向对象

this关键字,就是一个变量,用在方法中,可以拿到当前类的对象,即方法调用者的地址值。

构造器 其实是一种特殊的方法,但这个方法没有返回值类型,方法名必须和类名相同。在创建对象时,会调用构造器。也就是说 new Student() 就是在执行构造器,当构造器执行完了,也就意味着对象创建成功。

在设计一个类时,如果不写构造器,Java 会自动生成一个无参数构造器。一旦定义了有参数构造器,Java 就不再提供空参数构造器,此时建议加一个无参数构造器。

权限修饰符

封装设计规范用 8 个字总结,就是:合理隐藏、合理暴露,这就需要用到修饰符。

修饰符 本类 同一包下其他类 任意包下子类 任意包下任意类
private
缺省
protected
public

private < 缺省 < protected < public

实体 JavaBean

一般称之为实体类(封装数据的类),可以使用 ptg 插件 1 秒生成标准 JavaBean。

实体类只负责数据存取(除了给对象存、取值的方法就没有提供其他方法了),而对数据的处理交给其他类来完成,以实现数据和数据业务处理相分离(应用场景)。

package com.abc.domain;

public class Student {
    // 1. 成员变量私有化
    private String name;
    private int age;
    // 2. 空参、带参构造方法
    public Student() {}
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 3. 对私有成员变量提供 get 和 set 方法
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
}

工具类

如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个工具一样,所以把这样的类就叫做工具类。

工具类没有创建对象的需求,建议将工具类的构造器进行私有,这样别人就不能使用构造方法 new 对象了。

继承

重写的方法上面,可以加一个注解@Override,用于标注这个方法是重写的父类方法。使用这个方法重写小技巧,可以指定 java 编译器,检查方法重写的格式是否正确,代码可读性也会更好。

super关键字:代表继承的父类。子类构造器,都会先调用父类构造器,再执行自己。如果不想使用默认的 super() 方式调用父类构造器,可以手动使用 super(参数) 调用父类有参数构造器。

this 和 super 的用法:

访问本类成员:this. 成员变量    // 访问本类成员变量
  this. 成员方法    // 调用本类成员方法
  this()    // 调用本类空参数构造器
  this(参数)    // 调用本类有参数构造器
    
访问父类成员:super. 成员变量    // 访问父类成员变量
  super. 成员方法    // 调用父类成员方法
  super()    // 调用父类空参数构造器
  super(参数)    // 调用父类有参数构造器
    
注意:this 和 super 访问构造方法,只能用到构造方法的第一句,否则会报错。

多态

多态是在继承 / 实现情况下的一种现象,表现为:对象多态、行为多态 。注意多态是对象、行为的多态,Java 中的属性(成员变量) 不谈多态。

多态的表现形式:父类类型 对象名称 = 子类对象;,其实是 自动类型转换

多态的前提:

  • 有继承 / 实现关系
  • 有父类引用指向子类对象
  • 有方法重写

多态的好处:

  • 在多态形式下,右边代码是解耦合的,更便于扩展和维护
  • 定义方法时,使用父类类型作为形参,可以接收一切子类对象,扩展性更强,更便利

多态的弊端:

  • 在多态形式下,不能直接调用子类特有方法,但是转型后是可以调用的。转型就是 强制类型转换,即把父类变量转换为子类类型
  • 存在继承 / 实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。运行时,如果发现对象的真实类型与强转后的类型不同,就会报类型转换异常(ClassCastException)
  • 强转前,建议使用 instanceof 关键字,判断当前对象的真实类型

final 关键字:

  • 修饰类:表明该类是最终类,不能被继承
  • 修饰方法:表明该方法是最终方法,不能被重写
  • 修饰变量:该变量只能被赋值一次

常量:被 static final 修饰的成员变量,通常用于记录系统配置信息。

抽象类

关键字 abstract,是抽象的意思,可以修饰类也可以修饰方法:

  • 被 abstract 修饰的类,就是抽象类
  • 被 abstract 修饰的方法,就是抽象方法(不允许有方法体)

抽象类特点:

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  • 类该有的成员 (成员变量、方法、构造器) 抽象类都可以有
  • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
  • 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类

接口

关键字 interface,用来定义接口这种特殊结构。接口不能创建对象,是用来被类实现 (implements) 的,实现接口的类称为实现类。

public interface 接口名{
  // 成员变量(常量)// 成员方法(抽象方法)}

一个类可以实现多个接口(接口可理解成干爹),类实现接口必须重写所有接口的全部抽象方法,否则这个类也必须是抽象类。

接口和类的关系:

  • 类和类:继承关系,只能单继承,不能多继承,但可以多层继承
  • 类和接口:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
  • 接口和接口:继承关系,可以单继承,也可以多继承

内部类

内部类是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类内部,这个类就是内部类。

内部类有四种形式,分别是:

  • 成员内部类:类中的一个普通成员,类似于成员变量、成员方法。创建对象格式:Outer.Inner in = new Outer().new Inner();,外部类. 内部类 变量名 = new 外部类().new 内部类()
  • 静态内部类:其实就是在成员内部类前面加 static 关键字,属于外部类自己持有。创建对象:Outer.Inner in = new Outer.Inner();
  • 局部内部类:定义在方法中的类,和局部变量一样,只能在方法中有效。所以局部内部类局限性很强,一般在开发中不使用
  • 匿名内部类:一种特殊局部内部类,格式如下:
    new 父类 / 接口(参数值){
    @Override
    重写父类 / 接口方法;
    }

    匿名内部类本质上是一个子类,并会立即创建出一个子类对象。

枚举类

枚举是一种特殊类,格式如下:

public enum 枚举类名{枚举项 1, 枚举项 2, 枚举项 3;}

特点:

  • 枚举类第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象
  • 枚举类的构造器都是私有的,对外不能创建对象;枚举都是最终类,不能被继承
  • 枚举类中,从第二行开始,可以定义类的其他各种成员
  • 编译器为枚举类新增了几个方法,并且枚举类都是继承 java.lang.Enum 类,从 enum 类也会继承到一些方法

泛型

泛型限定:

  • <?> 表示任意类型
  • <? extends 数据类型 > 表示指定类型或者指定类型的子类
  • <? super 数据类型 > 表示指定类型或者指定类型的父类

泛型擦除,即泛型只能编译阶段有效,一旦编译成字节码,是不包含泛型的。而且泛型只支持引用数据类型,不支持基本数据类型。

包装类

包装类就是把基本类型数据包装成对象。

基本数据类型 对应包装类(引用数据类型)
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean

字符串

String 类常用方法

public class HelloWorld {public static void main(String[] args) {
        String s = "好 abc";
        // 字符串遍历
        for (int i = 0; i < s.length(); i++) {char ch = s.charAt(i);
            System.out.println(ch);
        }

        System.out.println("-------------------");

        // 字符串转字符数组,再遍历
        char[] chars = s.toCharArray();
        for (char aChar : chars) {System.out.println(aChar);
        }

        System.out.println("-------------------");

        System.out.println(createCode(4));
    }

    public static String createCode(int n) {
        String code = "";
        String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        Random r = new Random();
        for (int i = 0; i < n; i++) {int index = r.nextInt(data.length());
            code += data.charAt(index);
        }

        return code;
    }
}

StringBuilder

StringBuilder 可以看成是一个容器,创建之后里面的内容是可变的,作用是提高字符串的操作效率。

StringJoiner

使用 StringBuilder 拼接字符串时,代码写起来还是有一点麻烦,而 StringJoiner 号称是拼接神器,不仅效率高,而且代码简洁,JDK 8 才出现这个类。

public class HelloWorld {public static void main(String[] args) {
        // 参数 1:间隔符,参数 2:开头,参数 3:结尾
        StringJoiner s1 = new StringJoiner(",","[","]");
        s1.add("java1");
        s1.add("java2");
        s1.add("java3");
        System.out.println(s1); // [java1,java2,java3]
    }
}

JDK 8 日期类

表示日期、时间、日期时间的类:LocalDate、LocalTime、LocalDateTime 类,这三个类的用法套路都是一样的。

public class HelloWorld {public static void main(String[] args) {// 获取本地日期对象(不可变对象)
        LocalDate ld = LocalDate.now();
        System.out.println(ld); // 2024-09-10

        // 获取日期对象中的信息
        int year = ld.getYear(); // 年
        int month = ld.getMonthValue(); // 月(1-12)
        int day = ld.getDayOfMonth(); // 日
        int dayOfYear = ld.getDayOfYear();  // 一年中第几天
        int dayOfWeek = ld.getDayOfWeek().getValue(); // 星期几(1-7)
        System.out.println(year);
        System.out.println(month);
        System.out.println(day);
        System.out.println(dayOfYear);
        System.out.println(dayOfWeek);

        // 直接修改某个信息: withYear、withMonth、withDayOfYear、withDayOfMonth
        LocalDate ld2 = ld.withYear(2099);
        LocalDate ld3 = ld.withMonth(12);
        System.out.println(ld2); // 2099-09-10
        System.out.println(ld3); // 2024-12-10
        System.out.println(ld);  // 2024-09-10

        // 把某个信息加多少: plusYears、plusMonths、plusDays、plusWeeks
        LocalDate ld4 = ld.plusYears(2);
        LocalDate ld5 = ld.plusMonths(2);

        // 把某个信息减多少:minusYears、minusMonths、minusDays、minusWeeks
        LocalDate ld6 = ld.minusYears(2);
        LocalDate ld7 = ld.minusMonths(2);

        // 获取指定日期的 LocalDate 对象
        LocalDate ld8 = LocalDate.of(2099, 12, 12);
        LocalDate ld9 = LocalDate.of(2099, 12, 12);

        // 判断 2 个日期对象是否相等,在前还是在后:equals isBefore isAfter
        System.out.println(ld8.equals(ld9)); // true
        System.out.println(ld8.isAfter(ld)); // true
        System.out.println(ld8.isBefore(ld));// false

        // 获取本地日期和时间对象
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt); // 2024-09-10T22:32:28.566246800

        // 可以把 LocalDateTime 转换成 LocalDate 和 LocalTime
        LocalDate d = ldt.toLocalDate();
        LocalTime t = ldt.toLocalTime();
        System.out.println(d); // 2024-09-10
        System.out.println(t); // 22:33:39.302744400

        LocalDateTime dt = LocalDateTime.of(d, t);
        System.out.println(dt); // 2024-09-10T22:33:39.302744400
    }
}

时区

public class HelloWorld {public static void main(String[] args) {
        // ZoneId:时区 id
        ZoneId zoneId = ZoneId.systemDefault();
        System.out.println(zoneId.getId()); // Asia/Shanghai
        System.out.println(zoneId); // Asia/Shanghai

        // 获取 Java 支持的全部时区 id
        System.out.println(ZoneId.getAvailableZoneIds());

        // 把某个时区 id 封装成 ZoneId 对象
        ZoneId zoneId1 = ZoneId.of("America/New_York");

        // ZonedDateTime:带时区的时间
        // 获取某个时区的 ZonedDateTime 对象
        ZonedDateTime now = ZonedDateTime.now(zoneId1);
        System.out.println(now); // 2024-09-10T11:00:28.352739400-04:00[America/New_York]

        // 世界标准时间
        ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC());
        System.out.println(now1); // 2024-09-10T15:00:28.352739400Z

        // 获取系统默认时区的 ZonedDateTime 对象
        ZonedDateTime now2 = ZonedDateTime.now();
        System.out.println(now2); // 2024-09-10T23:00:28.352739400+08:00[Asia/Shanghai]
    }
}

Instant 类

通过获取 Instant 的对象可以拿到此刻的时间,由两部分组成:从 1970-01-01 00:00:00 开始到此刻的总秒数 + 纳秒数(1 秒 =1000 毫秒 1 毫秒 =1000 微秒 1 微秒 =1000 纳秒)。

public class HelloWorld {public static void main(String[] args) {
        // 创建 Instant 对象,获取此刻时间信息,不可变对象
        Instant now = Instant.now();
        System.out.println(now); // 2024-09-10T15:42:27.397201Z

        // 获取总秒数
        long second = now.getEpochSecond();
        System.out.println(second); // 1725982947
        // 不够 1 秒的纳秒数
        int nano = now.getNano();
        System.out.println(nano); // 397201000

        // Instant 对象的作用:做代码的性能分析,或者记录用户的操作时间点
        Instant now1 = Instant.now();
        // 代码执行。。。。Instant now2 = Instant.now();}
}

日期格式化类

DateTimeFormater,对日期进行格式化和解析。

public class HelloWorld {public static void main(String[] args) {
        // 创建日期时间格式化器对象
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy 年 MM 月 dd 日 HH:mm:ss");

        // 对时间进行格式化
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);

        String rs = formatter.format(now); // 正向格式化
        System.out.println(rs); // 2024 年 09 月 10 日 23:53:14

        // 格式化时间另一种方案
        String rs2 = now.format(formatter); // 反向格式化
        System.out.println(rs2); // 2024 年 09 月 10 日 23:53:14

        // 解析时间一般使用 LocalDateTime 提供的解析方法来解析
        String dateStr = "2029 年 12 月 12 日 12:12:12";
        LocalDateTime ldt = LocalDateTime.parse(dateStr, formatter);
        System.out.println(ldt); // 2029-12-12T12:12:12
    }
}

时间间隔

Period、Duration 这两个类用来对计算两个时间点的时间间隔。其中 Period 用来计算日期间隔(年、月、日),Duration 用来计算时间间隔(时、分、秒、纳秒)。

public class HelloWorld {public static void main(String[] args) {LocalDate start = LocalDate.of(2029, 8, 10);
        LocalDate end = LocalDate.of(2029, 12, 15);

        // 创建 Period 对象,封装两个日期对象
        Period period = Period.between(start, end);

        // 通过 Period 对象获取两个日期对象相差的信息
        System.out.println(period.getYears()); // 0
        System.out.println(period.getMonths());// 4
        System.out.println(period.getDays());  // 5

        LocalDateTime start2 = LocalDateTime.of(2025, 11, 11, 11, 10, 10);
        LocalDateTime end2 = LocalDateTime.of(2025, 11, 11, 11, 11, 11);
        // 得到 Duration 对象
        Duration duration = Duration.between(start2, end2);

        // 获取两个时间对象间隔的信息
        System.out.println(duration.toDays()); // 0 间隔多少天
        System.out.println(duration.toHours());// 0 间隔多少小时
        System.out.println(duration.toMinutes()); // 1 间隔多少分
        System.out.println(duration.toSeconds()); // 61 间隔多少秒
        System.out.println(duration.toMillis());// 61000 间隔多少毫秒
        System.out.println(duration.toNanos()); // 61000000000 间隔多少纳秒
    }
}

Arrays 类

public class HelloWorld {public static void main(String[] args) {int[] arr = {10, 20, 30, 40};
        System.out.println(Arrays.toString(arr)); // [10, 20, 30, 40]

        // 拷贝数组,包前不包后
        int[] arr2 = Arrays.copyOfRange(arr, 1, 3);
        System.out.println(Arrays.toString(arr2)); // [20, 30]

        // 拷贝数组,指定新数组长度
        int[] arr3 = Arrays.copyOf(arr, 5);
        System.out.println(Arrays.toString(arr3)); // [10, 20, 30, 40, 0]

        // 把数组中原数据改为新数据又存进去
        double[] prices = {99.8, 128, 100};
        // 把所有的价格都打八折,然后又存进去
        Arrays.setAll(prices, new IntToDoubleFunction() {
            @Override
            public double applyAsDouble(int value) {
                // value = 0  1  2
                return prices[value] * 0.8;
            }
        });
        System.out.println(Arrays.toString(prices)); // [79.84, 102.4, 80.0]
        // 使用 Lambda 表达式简化格式
        // Arrays.setAll(prices, value -> prices[value] * 0.8 );

        // 对数组进行排序(默认升序)
        Arrays.sort(prices);
        System.out.println(Arrays.toString(prices)); // [79.84, 80.0, 102.4]
    }
}

集合

集合包括:Collection 单列集合和 Map 双列集合。

Collection 接口:

  • List 接口:添加元素有序、可重复、有索引
    • ArrayList 实现类
    • LinkedList 实现类
  • Set 接口:添加元素无序、不重复、无索引
    • HashSet 实现类,子类 LinkedHashSet 实现类是有序的
    • TreeSet 实现类(默认按大小升序)

Map 接口:

  • HashMap<K, V> 实现类(无序、不重复、无索引),子类 LinkedHashMap(有序、不重复、无索引)
  • TreeMap<K, V> 实现类

遍历集合

迭代器就是一种集合的通用遍历方式。

public class HelloWorld {public static void main(String[] args) {Collection<String> c = new ArrayList<>();
        c.add("赵敏");
        c.add("小昭");
        c.add("灭绝");
        System.out.println(c); // [赵敏, 小昭, 灭绝]

        // 增强 for 循环(元素数据类型 变量名 : 数组或集合)
        for (String s : c) {System.out.println(s);
        }

        // 调用 forEach 方法
        c.forEach(System.out::println); //[赵敏, 小昭, 灭绝]
    }
}

List 集合

ArrayList 适合:根据索引查询数据;不适合:数据量大的同时,又要频繁的进行增删操作。

public class HelloWorld {public static void main(String[] args) {
        // 创建 ArrayList 集合对象
        ArrayList<String> list = new ArrayList<>();

        list.add("Python");
        list.add("Java");
        list.add("Python");
        System.out.println(list); // [Python, Java, Python]

        // 往集合某个索引位置处添加数据
        list.add(1, "MySQL");
        System.out.println(list); // [Python, MySQL, Java, Python]

        // 根据索引获取集合中某个索引位置处值
        String rs = list.get(1);
        System.out.println(rs); // MySQL

        // 获取集合大小,即存储的元素个数
        System.out.println(list.size()); // 4

        // 根据索引删除集合中的某个元素,返回被删除的元素
        System.out.println(list.remove(1)); // MySQL

        // 直接删除某个元素,默认删除第一次出现的数据,删除成功返回 true
        System.out.println(list.remove("Python"));  // true
        System.out.println(list); // [Java, Python]

        // 修改某个索引位置处的数据,修改后会返回原来的值
        System.out.println(list.set(1, "JS")); // Python
        System.out.println(list); // [Java, JS]

        list.removeIf(name -> name.contains("J"));
        System.out.println(list); // []}
}

Set 集合

HashSet 集合底层是基于哈希表实现的,哈希表根据 JDK 版本的不同有区别:

  • JDK 8 以前:哈希表 = 数组 + 链表
  • JDK 8 以后:哈希表 = 数组 + 链表 + 红黑树

HashSet 集合存储元素时,底层调用了元素的两个方法:一个是 hashCode 方法获取元素的 hashCode 值(哈希值);另一个是调用了元素的 equals 方法,用来比较新添加的元素和集合中已有的元素是否相同。

JDK 8 开始后,当链表长度超过 8,且数组长度 >=64 时,会把链表转成红黑树。

Map 集合

Map 集合遍历:

public class HelloWorld {public static void main(String[] args) {Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 169.8);
        map.put("孙悟空", 165.8);
        map.put("至尊宝", 169.5);
        System.out.println(map);

        // 遍历 map 集合,传递 Lambda 表达式
        map.forEach((k, v) -> System.out.println(k + "-->" + v));
    }
}

Stream 流

也叫 Stream API,是从 JDK 8 以后的一个新特性,专业用于对集合或数组进行便捷操作。

获取 Stream 流:

public class HelloWorld {public static void main(String[] args) {
        // 1、获取 List 集合 Stream 流
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰", "张无忌", "周芷若");
        Stream<String> stream = names.stream();
        System.out.println(stream);

        // 2、获取 Set 集合 Stream 流
        Set<String> set = new HashSet<>();
        Collections.addAll(set, "刘德华", "张德玉", "蜘蛛精");
        Stream<String> stream1 = set.stream();
        stream1.filter(s -> s.contains("德")).forEach(System.out::println);

        // 3、获取 Map 集合的 Stream 流
        Map<String, Double> map = new HashMap<>();
        map.put("古力娜扎", 172.3);
        map.put("迪丽热巴", 168.3);
        map.put("马尔扎巴", 166.3);

        Set<String> keys = map.keySet();
        Stream<String> ks = keys.stream();
        System.out.println(ks);

        Collection<Double> values = map.values();
        Stream<Double> vs = values.stream();
        System.out.println(vs);

        Set<Map.Entry<String, Double>> entries = map.entrySet();
        Stream<Map.Entry<String, Double>> kvs = entries.stream();
        kvs.filter(e -> e.getKey().contains("巴"))
                .forEach(e -> System.out.println(e.getKey() + "-->" + e.getValue()));

        // 4、获取数组 Stream 流
        String[] names2 = {"张翠山", "东方不败", "唐大山"};
        Stream<String> s1 = Arrays.stream(names2);
        Stream<String> s2 = Stream.of(names2);
        System.out.println(s1);
        System.out.println(s2);
    }
}

Stream 流中间方法可以继续调用,这样一来就可以支持链式编程(或者叫流式编程)。

Stream 流终结方法,调用完之后,其结果就不再是 Stream 流了,所以不支持链式编程。

IO 流

  • 输入流(读数据):磁盘 / 网络 –> 内存
  • 输出流(写数据):内存 –> 磁盘 / 网络

缓冲流

缓冲字节流(自带 8KB 缓冲池):

public class HelloWorld {public static void main(String[] args) {
        try (InputStream is = new FileInputStream("helloworld-app/src/abc.txt");
                // 1、定义一个字节缓冲输入流包装原始字节输入流
                InputStream bis = new BufferedInputStream(is);

                OutputStream os = new FileOutputStream("helloworld-app/src/abc2.txt");
                // 2、定义一个字节缓冲输出流包装原始字节输出流
                OutputStream bos = new BufferedOutputStream(os);
        ){byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1){bos.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");

        } catch (Exception e) {e.printStackTrace();
        }
    }
}

转换流

有两种转换流 InputStreamReader,OutputStreamWriter,它们可以将字节流转换为字符流,并且可以指定编码方案。

序列化流

字节流是以字节为单位来读写数据、字符流是以字符为单位来读写数据、而对象流是以对象为单位来读写数据。也就是把对象当做一个整体,可以写一个对象到文件(序列化),也可以从文件中把对象读取出来(反序列化)。

IO 框架

为了简化对 IO 操作,Apache 开源基金组织提供了一组有关 IO 流小框架(名字叫 Commons-IO),可以提高 IO 流的开发效率。

引入 jar 包(提供的工具类叫 FileUtils),具体步骤如下:

  1. 在模块目录下,新建 lib 文件夹
  2. 把 jar 包复制粘贴到 lib 文件夹下
  3. 选择 lib 下的 jar 包,右键点击 Add As Library,然后就可以用了
public class HelloWorld {public static void main(String[] args) throws IOException {
        // 复制文件
        FileUtils.copyFile(new File("helloworld-app/src/abc.txt"), new File("helloworld-app/src/abc2.txt"));

        // 复制文件夹
        FileUtils.copyDirectory(new File("D:\\ 私人珍藏"), new File("D:\\ 私人珍藏 2"));

        // 删除文件夹
        FileUtils.deleteDirectory(new File("D:\\ 私人珍藏 2"));

        // Java 原生提供的一行代码搞定很多事情
        Files.copy(Path.of("helloworld-app/src/abc.txt"), Path.of("helloworld-app/src/abc2.txt"));
        System.out.println(Files.readString(Path.of("helloworld-app/src/abc.txt")));
    }
}

Lambda 表达式

作用:用于简化匿名内部类的代码书写,JDK 8 新语法形式。Lambda 表达式只能简化函数式接口的匿名内部类。

函数式接口:

  • 有且仅有一个抽象方法的接口
  • 大部分函数式接口,上面都可能会有一个 @FunctionalInterface 注解,有该注解的接口必定是函数式接口

静态方法引用

用来进一步简化 Lambda 表达式,它简化的更加过分。
Student.java:

package com.abc.hello;

public class Student implements Comparable<Student> {
    private String name;
    private double height;
    private int age;

    public Student(String name, double height, int age) {
        this.name = name;
        this.height = height;
        this.age = age;
    }

    public int getAge() {return age;}

    @Override
    public int compareTo(Student o) {
        return this.age - o.age; // 按照年龄升序排列
        // return o.age - this.age; // 按照年龄降序排列
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", height=" + height +
                ", age=" + age +
                '}';
    }
}

CompareByAge.java:

package com.abc.hello;

public class CompareByAge {public static int compareByAge(Student o1, Student o2) {return o1.getAge() - o2.getAge(); // 升序排序规则}
}

HelloWorld.java:

package com.abc.hello;

import java.util.Arrays;

public class HelloWorld {public static void main(String[] args) {Student[] students = new Student[3];
        students[0] = new Student("蜘蛛精", 169.5, 23);
        students[1] = new Student("紫霞", 163.8, 26);
        students[2] = new Student("至尊宝", 167.5, 24);
        System.out.println(Arrays.toString(students));

        // 原始写法:对数组中学生对象,按照年龄升序排序
//        Arrays.sort(students, new Comparator<Student>() {
//            @Override
//            public int compare(Student o1, Student o2) {//                return o1.getAge() - o2.getAge(); // 按照年龄升序排序
//            }
//        });

        // 使用 Lambda 简化后形式
//        Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());

//        Arrays.sort(students, (o1, o2) -> CompareByData.compareByAge(o1, o2));

        // 静态方法引用:类名:: 方法名。实际上是用类名调用方法,但把参数给省略了
        Arrays.sort(students, CompareByAge::compareByAge);

        System.out.println(Arrays.toString(students));
    }
}

异常

异常体系:

  • Error:系统级别错误
  • Exception:异常
    • 运行时异常:RuntimeException
    • 编程时异常:Exception

特殊文本文件

两种特殊的文本文件,一种是 properties 文件,一种是 xml 文件。

XML 文件

如果 XML 标签文本中有特殊字符,需要用一些占位符代替:

&lt;  表示 <
&gt;  表示 >
&amp; 表示 &
&apos; 表示 '&quot; 表示"
<data> 3 &lt; 2 &amp;&amp; 5 &gt; 4 </data>

如果标签文本中,出现大量的特殊字符,不想使用特殊字符,此时可以用 CDATA 区,格式如下:

<data>
    <![CDATA[3 < 2 && 5 > 4]]>
</data>

读取 XML 文件中的数据,称为 XML 解析。好用的 XML 解析框架,最知名的是 DOM4J(第三方开发)。

DOM4J 也提供了往 XML 文件中写标签的方法,但是用起来比较麻烦,不建议使用。可以使用 StringBuilder 按照标签的格式拼接,然后再使用 BufferedWriter 写到 XML 文件中去就可以了。

XML 约束有两种,一种是 DTD 约束(.dtd 结尾文件)、一种是 Schame 约束(.xsd 结尾文件)。Schame 可以约束 XML 文件的编写和数据类型

日志技术

推荐使用 Logback 日志框架,是基于 SLF4J 日志规范实现的框架,分为 3 个模块:logback-core(必须有)、logback-classes(必须有)、logback-access(可选)。

要使用 Logback 日志框架,至少需要在项目中整合如下 3 个模块:

  • slf4j-api:日志接口
  • logback-core
  • logback-classes

正文完
 0
三毛笔记
版权声明:本站原创文章,由 三毛笔记 于2023-10-09发表,共计18614字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)